Contents
  1. 1. 简介
  2. 2. 使用Java自带的注解
    1. 2.1. @Deprecated
    2. 2.2. @Override
    3. 2.3. @SuppressWarnings
      1. 2.3.1. SuppressWarnings注解的常见参数值的简单说明:
    4. 2.4. 自定义注解
      1. 2.4.1. 简单声明
      2. 2.4.2. 注解参数的可支持数据类型:
  3. 3. 元注解
    1. 3.1. @Documented
    2. 3.2. @Retention
      1. 3.2.1. 按照Retention的三种枚举类型,注解分为三种级别,每个级别有其特定的功能
    3. 3.3. @Target
      1. 3.3.1. 取值(ElementType)有:
    4. 3.4. @Inherited

参考: http://blog.csdn.net/duo2005duo/article/details/50505884

http://www.cnblogs.com/peida/archive/2013/04/24/3036689.html

简介

  注解(annotation)是JDK5之后引进的新特性,是一种特殊的注释,之所以说它特殊是因为不同于普通注释(comment)能存在于源码,而且还能存在编译期跟运行期,会最终编译成一个.class文件,所以注解能有比普通注释更多的功能

使用Java自带的注解

这里只讲Java5 自带的3种注解

@Deprecated

  • @Deprecated 表明当前的元素已经不推荐使用

  当一个类型或者类型成员使用@Deprecated修饰的话,编译器将不鼓励使用这个被标注的程序元素。而且这种修饰具有一定的 “延续性”:如果我们在代码中通过继承或者覆盖的方式使用了这个过时的类型或者成员,虽然继承或者覆盖后的类型或者成员并不是被声明为 @Deprecated,但编译器仍然要报警。
  
  值得注意,@Deprecated这个annotation类型和javadoc中的 @deprecated这个tag是有区别的:前者是java编译器识别的,而后者是被javadoc工具所识别用来生成文档(包含程序成员为什么已经过 时、它应当如何被禁止或者替代的描述)。
  
在java5.0,java编译器仍然象其从前版本那样寻找@deprecated这个javadoc tag,并使用它们产生警告信息。但是这种状况将在后续版本中改变,我们应在现在就开始使用@Deprecated来修饰过时的方法而不是 @deprecated javadoc tag。

@Deprecated注解标示方法过期,同时在方法注释中用@deprecated tag 标示该方法已经过时,

1
2
3
4
5
6
7
8
/**
* 一般来说使用@Deprecated注解的方法要加上一下的javadoc文档
* @deprecated 这里要写明因为什么原因抛弃了使用该方法,推荐使用什么方法代替
*/

@Deprecated
public void badMethod(){ //已经不推荐使用的方法
...
}

@Override

  • @Override 表明当前方法是覆盖了父类方法,添加@Overrride是一种必须的编程习惯,因为帮助你找到代码中的错误,考虑以下情况
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Fruit {

public void displayName(){
System.out.println("水果的名字是:*****");
}
}

class Orange extends Fruit {
@Override //重写
public void displayName(){
System.out.println("水果的名字是:桔子");
}
}

@SuppressWarnings

  • @SuppressWarnings 关闭不当的编译器警告信息,如下
1
2
3
4
5
6
7
8
class A{
final void doSth(){
//添加说明抑制警告的原因:A的getClass() 必然是Class<A>
@SuppressWarnings(unchecked)
Class<A> a=(Class<A>)getClass() //有unchecked警告
...
}
}

  @SuppressWarnings 被用于有选择的关闭编译器对类、方法、成员变量、变量初始化的警告。在java5.0,sun提供的javac编译器为我们提供了-Xlint选项来使编译器对合法的程序代码提出警告,此种警告从某种程度上代表了程序错误。例如当我们使用一个generic collection类而又没有提供它的类型时,编译器将提示出”unchecked warning”的警告。通常当这种情况发生时,我们就需要查找引起警告的代码。如果它真的表示错误,我们就需要纠正它。例如如果警告信息表明我们代码中的switch语句没有覆盖所有可能的case,那么我们就应增加一个默认的case来避免这种警告。
  有时我们无法避免这种警告,例如,我们使用必须和非generic的旧代码交互的generic collection类时,我们不能避免这个unchecked warning。此时@SuppressWarning就要派上用场了,在调用的方法前增加@SuppressWarnings修饰,告诉编译器停止对此方法的警告。
  SuppressWarning不是一个标记注解。它有一个类型为String[]的成员,这个成员的值为被禁止的警告名。对于javac编译器来讲,被-Xlint选项有效的警告 名也同样对@SuppressWarings有效,同时编译器忽略掉无法识别的警告名。
  annotation语法允许在annotation名后跟括号,括号中是使用逗号分割的name=value对用于为annotation的成员赋值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class FruitService {

@SuppressWarnings(value={ "rawtypes", "unchecked" })
public static List<Fruit> getFruitList(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}

@SuppressWarnings({ "rawtypes", "unchecked" })
public static List<Fruit> getFruit(){
List<Fruit> fruitList=new ArrayList();
return fruitList;
}

@SuppressWarnings("unused")
public static void main(String[] args){
List<String> strList=new ArrayList<String>();
}
}

  在这个例子中SuppressWarnings annotation类型只定义了一个单一的成员,所以只有一个简单的value={…}作为name=value对。又由于成员值是一个数组,故使用大括号来声明数组值。注意:我们可以在下面的情况中缩写annotation:当annotation只有单一成员,并成员命名为”value=”。这时可以省去”value=”。比如将上面方法getFruit()的SuppressWarnings annotation就是缩写的。

SuppressWarnings注解的常见参数值的简单说明:

    1.deprecation:使用了不赞成使用的类或方法时的警告;

    2.unchecked:执行了未检查的转换时的警告,例如当使用集合时没有用泛型 (Generics) 来指定集合保存的类型;

    3.fallthrough:当 Switch 程序块直接通往下一种情况而没有 Break 时的警告;

    4.path:在类路径、源文件路径等中有不存在的路径时的警告;

    5.serial:当在可序列化的类上缺少 serialVersionUID 定
义时的警告;

    6.finally:任何 finally 子句不能正常完成时的警告;

    7.all:关于以上所有情况的警告。

自定义注解

简单声明

  • 元素不能有不确定的值,即要么有默认值,要么在使用注解的时候提供元素的值。而且元素不能使用null作为默认值。所以我们应该定义一些特殊的值,来表示某个元素不存在

  • 注解在只有一个元素且该元素的名称是value的情况下,在使用注解的时候可以省略“value=”,直接写需要的值即可。

  • 注解的声明相对简单,有点类似接口的写法
    其中的每一个方法实际上是声明了一个配置参数。方法的名称就是参数的名称,返回值类型就是参数的类型(返回值类型只能是基本类型、Class、String、enum)。可以通过default来声明参数的默认值。

注解参数的可支持数据类型:

    1.所有基本数据类型(int,float,boolean,byte,double,char,long,short)
    2.String类型
    3.Class类型
    4.enum类型
    5.Annotation类型
    6.以上所有类型的数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Public @interface Empty{
String field1() default "it's empty"; //default 设置默认值
}
@Empty //使用 default默认值"it's empty"
class C{
}

//如果没有使用default设置默认值的话,field必须显式传入参数,如下
@Empty(field1="empty A")
class A{
}

@interface Value{
String value();
String other() default "it's great";
}
@Value("empty B") //当注解有一个方法value时,没有其他方法或者其他方法有默认值,不需要指明具体方法
class B{
}

  以上是Java自带的一些注解,Java同时提供了另外一类注解,叫元注解。元注解可以用来设置注解,自定义注解一般都会使用到元注解

元注解

  ‘普通注解’只能用来注解’代码’,而’元注解’只能用来注解 ‘普通注解’。
JDK5时支持的元注解有@Documented @Retention @Target @Inherited,接下来分别介绍它们修饰注解的效果

@Documented

  • @Documented让注解自动加入Javadoc

  @Documented用于描述其它类型的annotation应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。Documented是一个标记注解,没有成员。

1
2
3
4
5
6
7
8
@Documented
@interface DocumentedAnnotation{
}
@interface UnDocumentedAnnotation{
}
@DocumentedAnnotation
@UnDocumentedAnnotation
class A{}

  代码中注解DocumentedAnnotation添加了元注解@Documented,而UnDocumentedAnnotation则没有添加,通过’javadoc A.java’命令生成Javadoc如下图。可以看到,在A类的Javadoc中只能看到@DocumentedAnnotation注解,但是无法看到@UnDocumentedAnnotation注解

@Retention

  • 用@Retention设置注解的级别

  作用:表示需要在什么级别保存该注释信息,用于描述注解的生命周期(即:被描述的注解在什么范围内有效)

1
2
3
//RetentionPolicy是一个枚举类,默认是CLASS,此外还可选择SOURCE,RUNTIME
@Retention(RetentionPolicy.CLASS)
@interface ClassRetentionAnnotation{}

按照Retention的三种枚举类型,注解分为三种级别,每个级别有其特定的功能

  • SOURCE代表源码级别,注解只存在源码(源文件)中,其功能是与编译器交互,用于代码检测,如@Override,@SuppressWarings,许多框架如Dragger就是使用这个级别的注解,这个级别的框架额外效率损耗发生在编译时
  • CLASS代表字节码级别,注解存在源码与字节码文件(class文件)中,主要用于编译时生成而外的文件,如XML,Java文件等,这个级别需要添加JVM加载时候的代理(javaagent ),使用代理来动态修改字节码文件(由于Android虚拟机并不支持所以本专题不会再做介绍,在Android中可以使用aspectJ来实现类似这个级别的功能)
  • RUNTIME代表运行时级别,注解存在源码,字节码与Java虚拟机中,主要用于运行时反射获取相关信息,许多框架如OrmLite就是使用这个级别的注解,这个级别的框架额外的效率损耗发生在程序运行时

@Target

  • @Target限制注解的元素种类

  不加元注解@Target的情况下,注解可以修饰各种元素,比如可以修饰类,可以修饰变量,可以修饰方法等,但是如果要限制注解的元素种类,比如限制只能修饰方法,则需要加入@Target,一下是@Override的源码

取值(ElementType)有:

    1.CONSTRUCTOR:用于描述构造器

    2.FIELD:用于描述域

    3.LOCAL_VARIABLE:用于描述局部变量

    4.METHOD:用于描述方法

    5.PACKAGE:用于描述包

    6.PARAMETER:用于描述参数

    7.TYPE: 用于描述 类、 接口(包括注解类型) 或 enum 声明

1
2
3
4
@Target(ElementType.METHOD) //限制了注解只能修饰方法,ElementType是一个枚举类
@Retention(RetentionPolicy.SOURCE)
public @interface Override{
}

可以看到@Override源码中已经限定了只能修饰方法。

@Inherited

  • @Inherited可以让注解类似被继承一样,但是这并不是真的继承。通过使用@Inherited,只可以让子类类对象使用getAnnotations()反射获取父类被@Inherited修饰的注解。

注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Inheritable{
}
@interface UnInheritable{
}
public class Test{
@UnInheritable
@Inheritable
public static class Super{
}
public static class Sub extends Super {
}
public static void main(String... args){
Super instance=new Sub();
System.out.println(Arrays.toString(instance.getClass().getAnnotations()));
}
}

输出:[Inheritable]
Contents
  1. 1. 简介
  2. 2. 使用Java自带的注解
    1. 2.1. @Deprecated
    2. 2.2. @Override
    3. 2.3. @SuppressWarnings
      1. 2.3.1. SuppressWarnings注解的常见参数值的简单说明:
    4. 2.4. 自定义注解
      1. 2.4.1. 简单声明
      2. 2.4.2. 注解参数的可支持数据类型:
  3. 3. 元注解
    1. 3.1. @Documented
    2. 3.2. @Retention
      1. 3.2.1. 按照Retention的三种枚举类型,注解分为三种级别,每个级别有其特定的功能
    3. 3.3. @Target
      1. 3.3.1. 取值(ElementType)有:
    4. 3.4. @Inherited